package com.javacoo.swing.core.http.httpclient;


import com.alibaba.fastjson.JSON;
import com.javacoo.swing.api.http.HttpHandler;
import com.javacoo.swing.api.http.RemoteSetting;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.config.AuthSchemes;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.DefaultHttpRequestRetryHandler;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicNameValuePair;

import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;

/**
 * HTTP连接器
 * <p>
 * 说明:
 * </p>
 * <li>基于HttpClient</li>
 * 
 * @author DuanYong
 * @since 2017年3月3日上午10:17:31
 */
@Slf4j
public class HttpClientHandler implements HttpHandler {
	private static final String UTF8 = "UTF-8";
	/** 远程配置 */
	private RemoteSetting remoteSetting;
	/** HttpClient */
	private CloseableHttpClient httpClient = null;

	@Override
	public HttpHandler init(RemoteSetting remoteSetting) {
		this.remoteSetting = remoteSetting;
		httpClient = getHttpClient();
		return this;
	}

	@Override
	public String post(String address, Map<String,Object> data,String cookie) {
		return doPost(address, data,cookie);
	}

	@Override
	public String get(String address, String cookie) {
		return doGet(address,cookie);
	}

	/**
	 * 执行post请求
	 * <p>
	 * 说明:
	 * </p>
	 * <li></li>
	 * 
	 * @author DuanYong
	 * @param url
	 *            请求地址
	 * @param data
	 *            参数
	 * @return 响应结果
	 * @since 2018年12月19日上午10:09:17
	 */
	private String doPost(String url, Map<String,Object> data,String cookie) {
		HttpPost post = new HttpPost(url);
		CloseableHttpResponse httpResponse = null;
		Long startTime = System.currentTimeMillis();
		try {
			try {
				post.setHeader("Content-Type", remoteSetting.getContentType());
				post.setHeader("Accept",remoteSetting.getAccept());
				post.setHeader("Accept-Language",remoteSetting.getAcceptLanguage());
				post.setHeader("User-Agent", remoteSetting.getUserAgent());
				post.setHeader("Accept-Charset",remoteSetting.getAcceptCharset());
				post.setHeader("Keep-Alive", remoteSetting.getKeepAlive());
				post.setHeader("Connection", remoteSetting.getConnection());
				post.setHeader("Cache-Control", remoteSetting.getCacheControl());
				post.setHeader("Host", remoteSetting.getHost());
				post.setHeader("Referer", remoteSetting.getReferer());
				post.setHeader("Cookie",cookie);
				post.setHeader("TE","Trailers");
				post.setHeader("X-Requested-With","XMLHttpRequest");

				post.setEntity(getStringEntity(data));
				httpResponse = httpClient.execute(post);
			} catch (Exception e) {
				log.error("发送远程服务调用请求时发生异常,请求URL:{},POST参数:{},信息：",url,data,e);
				e.printStackTrace();
			}
			log.info("{},(参数:{}),请求完成,耗时->{}秒",url,data,(System.currentTimeMillis() - startTime) / 1000.0);
			// 读取响应结果
			try {
				return getResponse(httpResponse);
			} catch (Exception e) {
				e.printStackTrace();
			}
		} finally {
			try {
				if (null != httpResponse) {
					httpResponse.close();
				}
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		return "";
	}
	private HttpEntity getUrlEncodedFormEntity(Map<String, Object> data) throws UnsupportedEncodingException {
		// 用于存放表单数据
		List<BasicNameValuePair> pairs = new ArrayList<>(10);
		// 遍历map 将其中的数据转化为表单数据
		for (Map.Entry<String, Object> entry : data.entrySet()) {
			if(entry.getValue() == null){
				continue;
			}
			pairs.add(new BasicNameValuePair(entry.getKey(), entry.getValue().toString()));
		}
		// 对表单数据进行url编码
		return new UrlEncodedFormEntity(pairs);
	}
	private HttpEntity getStringEntity(Map<String,Object> data) {
		String jsonStr = "";
		try {
			jsonStr  = JSON.toJSONString(data);
		}catch(Exception e){
			e.printStackTrace();
		}
		return new StringEntity(jsonStr,"UTF-8");
	}
	/**
	 * 执行get请求
	 * <p>
	 * 说明:
	 * </p>
	 * <li></li>
	 * @author DuanYong
	 * @param url 请求地址
	 * @param cookie cookie
	 * @return 响应数据
	 * @since 2018年12月19日上午10:20:44
	 */
	private String doGet(String url,String cookie) {
		HttpGet get = new HttpGet(url);
		CloseableHttpResponse httpResponse = null;
		Long startTime = System.currentTimeMillis();
		try {
			try {
				get.setHeader("Content-Type", remoteSetting.getContentType());
				get.setHeader("Accept",remoteSetting.getAccept());
				get.setHeader("Accept-Language",remoteSetting.getAcceptLanguage());
				get.setHeader("User-Agent", remoteSetting.getUserAgent());
				get.setHeader("Accept-Charset",remoteSetting.getAcceptCharset());
				get.setHeader("Keep-Alive", remoteSetting.getKeepAlive());
				get.setHeader("Connection", remoteSetting.getConnection());
				get.setHeader("Cache-Control", remoteSetting.getCacheControl());
				get.setHeader("Cookie",cookie);
				httpResponse = httpClient.execute(get);
			} catch (Exception e) {
				log.error("发送远程服务调用请求时发生异常,请求URL:{},信息",url, e);
				e.printStackTrace();
			}
			if (log.isErrorEnabled()) {
				log.error("{},请求完成,耗时->{}秒",url,(System.currentTimeMillis() - startTime) / 1000.0);
			}
			/**
			 * 读取响应结果
			 */
			try {
				return getResponse(httpResponse);
			} catch (Exception e) {
				e.printStackTrace();
			}
		} finally {
			try {
				if (null != httpResponse) {
					httpResponse.close();
				}
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		return "";
	}

	/**
	 * 获取HttpClient
	 * <p>
	 * 说明:
	 * </p>
	 * <li></li>
	 * 
	 * @author DuanYong
	 * @return
	 * @since 2017年6月21日上午9:43:20
	 */
	private CloseableHttpClient getHttpClient() {
		CloseableHttpClient client;
		// 设置协议http和https对应的处理socket链接工厂的对象
		Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory> create()
				.register("http", PlainConnectionSocketFactory.INSTANCE)
				.register("https", getSSLConnectionSocketFactory()).build();
		// 初始化线程池
		PoolingHttpClientConnectionManager pccm = new PoolingHttpClientConnectionManager(socketFactoryRegistry);
		pccm.setMaxTotal(remoteSetting.getMaxTotal());
		// 单路由最大并发数
		pccm.setDefaultMaxPerRoute(remoteSetting.getDefaultMaxPerRoute());
		// 配置请求的超时设置
		RequestConfig requestConfig = RequestConfig.custom().setExpectContinueEnabled(true)
				.setTargetPreferredAuthSchemes(Arrays.asList(AuthSchemes.NTLM, AuthSchemes.DIGEST))
				.setProxyPreferredAuthSchemes(Arrays.asList(AuthSchemes.BASIC))
				.setSocketTimeout(remoteSetting.getSocketTimeout())
				.setConnectTimeout(remoteSetting.getConnectionTimeout())
				.setConnectionRequestTimeout(remoteSetting.getConnectionRequestTimeout()).build();
		// 构建
		client = HttpClients.custom().setConnectionManager(pccm).setDefaultRequestConfig(requestConfig)
				.setRetryHandler(new DefaultHttpRequestRetryHandler()).build();// 默认失败后重发3次
		return client;
	}

	/**
	 * 获取SSLConnectionSocketFactory
	 * <p>
	 * 说明:
	 * </p>
	 * <li></li>
	 * 
	 * @author DuanYong
	 * @return
	 * @since 2017年8月31日上午11:40:30
	 */
	private SSLConnectionSocketFactory getSSLConnectionSocketFactory() {
		try {
			return new SSLConnectionSocketFactory(createIgnoreVerifySSL());
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}

	/**
	 * 绕过验证
	 * <p>
	 * 说明:
	 * </p>
	 * <li></li>
	 * 
	 * @author DuanYong
	 * @return
	 * @throws NoSuchAlgorithmException
	 * @throws KeyManagementException
	 * @since 2017年8月31日下午3:40:59
	 */
	private static SSLContext createIgnoreVerifySSL() throws NoSuchAlgorithmException, KeyManagementException {
		SSLContext sc = SSLContext.getInstance("TLS");
		// 实现一个X509TrustManager接口，用于绕过验证，不用修改里面的方法
		X509TrustManager trustManager = new X509TrustManager() {
			@Override
			public void checkClientTrusted(java.security.cert.X509Certificate[] paramArrayOfX509Certificate,
					String paramString) throws CertificateException {
			}

			@Override
			public void checkServerTrusted(java.security.cert.X509Certificate[] paramArrayOfX509Certificate,
					String paramString) throws CertificateException {
			}

			@Override
			public java.security.cert.X509Certificate[] getAcceptedIssuers() {
				return null;
			}
		};

		sc.init(null, new TrustManager[] { trustManager }, null);
		return sc;
	}

	/**
	 * 获取响应结果
	 * <p>
	 * 说明:
	 * </p>
	 * <li></li>
	 * 
	 * @author DuanYong
	 * @param httpResponse
	 * @return
	 * @throws IOException
	 * @throws UnsupportedEncodingException
	 * @since 2017年6月21日上午10:26:11
	 */
	private String getResponse(HttpResponse httpResponse) throws IOException, UnsupportedEncodingException {
		if(null == httpResponse){
			return "";
		}
		HttpEntity entity = httpResponse.getEntity();
		log.info("响应结果,ContentType:{}",entity.getContentType().getValue());
		log.info("响应结果,ContentLength:{}",entity.getContentLength());
		InputStream is = entity.getContent();
		ByteArrayOutputStream out = new ByteArrayOutputStream(4096);
		IOUtils.copy(is, out);
		String responseCharset = ((entity.getContentEncoding() == null || entity.getContentEncoding().getValue() == null) ? UTF8 : entity.getContentEncoding().getValue());
		String result = new String(out.toByteArray(), responseCharset);
		if (log.isInfoEnabled()) {
			log.info("响应结果:{}",result);
		}
		return result;
	}

}
